package com.yhd.registry;

import com.yhd.protocol.InvokerProtocol;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.sql.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 二十
 * @since 2022/5/2 12:05 上午
 */
public class RpcRegistryHandler extends ChannelInboundHandlerAdapter {

    private String basePackage;

    public static Map<String,Object> registryMap = new ConcurrentHashMap<>();

    private List<String> classNames = new ArrayList<>();

    public RpcRegistryHandler(String basePackage) {

        this.basePackage = basePackage;

        scanner(basePackage);

        doRegister();
    }




    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Object result = null;
        InvokerProtocol request  = (InvokerProtocol) msg;

        if (registryMap.containsKey(request.getClassName())){
            Object clazz = registryMap.get(request.getClassName());
            Method method = clazz.getClass().getMethod(request.getMethodName(), request.getParams());
            result =method.invoke(clazz,request.getValues());
        }
        ctx.writeAndFlush(result);
        ctx.close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    private void doRegister(){
        if (classNames.size()==0){
            return ;
        }
        for (String className : classNames) {
            try {
                Class<?> clazz = Class.forName(className);
                Class<?> interfaceClazz = clazz.getInterfaces()[0];
                registryMap.put(interfaceClazz.getName(),clazz.newInstance());
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    /**
     * 这里因为是项目在本地，所以不搞得那么复杂了，如果是真实的注册中心，其实这里可以考虑对外暴露一个http的接口，
     * 在其他服务启动的时候通过http请求来注册。
     * @param basePackage
     */
    private void scanner(String basePackage) {
        URL url = this.getClass().getClassLoader().getResource(basePackage);
        File dir = new File(url.getFile());
        //if is dir , continue
        Arrays.stream(dir.listFiles()).forEach(file -> {
            if (file.isDirectory())
                scanner(basePackage + "." + file.getName());
            else
                classNames.add(basePackage + "." + file.getName().replace(".class", "").trim());
        });
    }
}
